#!/usr/bin/python

import os
import email

debug = True
def dprint (msg):
    global debug
    if debug:
        print msg

class EmailAddress:
    def __init__ (self, email_str):
        self.email_str = email_str
        # TODO: parse email_str into below components
        self.email = None
        self.name = None
        self.domain = None
        self.id = None
        return


class Label:
    def __init__ (self, label):
        self.label = label
        self.emails = []
        return


class Email:
    def __init__ (self, filename):
        self.filename = filename
        self.headers = {}
        return

    def _read_headers (self):
        headers_str = ''
        fd = None
        try:
#            fd = os.open (self.filename, os.O_RDONLY)
            fd = file (self.filename)
        except:
            dprint ("Unable to open file: %s" % self.filename)
            return False

        while True:
            next_str = fd.readline ()
            if next_str.strip () == '':
                break
            headers_str = headers_str + next_str
        msg = email.message_from_string (headers_str)
        
#        msg = email.message_from_file (fd)
        fd.close ()
        
        self.headers = {}

        for key in msg.keys ():
            self.headers[key.upper ()] = msg[key]

        return True

    def read_headers (self):
        if not self.headers:
            self._read_headers ()
            
        self.message_id = self.filename
        if self.headers.has_key ('MESSAGE-ID'):
            self.message_id = self.headers['MESSAGE-ID']

        self.from_email = 'unknown@domain'
        if self.headers.has_key ('FROM'):
            self.from_email = EmailAddress (self.headers['FROM'])

        self.in_reply_to = []
        if self.headers.has_key ('IN-REPLY-TO'):
            self.in_reply_to = self.headers['IN-REPLY-TO'].split ()

        self.references = []
        if self.headers.has_key ('REFERENCES'):
            self.references = self.headers['REFERENCES'].split ()

        self.thread_topic = None
        if self.headers.has_key ('THREAD-TOPIC'):
            self.thread_topic = self.headers['THREAD-TOPIC']

        self.subject = None
        if self.headers.has_key ('SUBJECT'):
            self.subject = self.headers['SUBJECT']

        return True

        
class Conversation:
    def __init__ (self, email):
        self.principle_email = email
        self.emails = [email]


class Maildir:
    def __init__ (self, dirname):
        self.dirname = dirname
        self.emails = {}
        self.labels = {}
        self.conversation = {}
        self.conversations = []
        return

    def read_headers (self):
        if not os.path.isdir (self.dirname):
            return False

        curdir = os.path.join (self.dirname, "cur")
        if not os.path.isdir (curdir):
            return False

        newdir = os.path.join (self.dirname, "new")
        if not os.path.isdir (newdir):
            return False

        self.emails = {}
        mail_count = 0

        all_files = map (lambda d: "cur/" + d,
                         os.listdir (curdir)) + map (lambda d: "new/" + d,
                                                     os.listdir (newdir))
        for filename in all_files:
            filepath = os.path.join (self.dirname, filename)
            mail = Email (filepath)
            
            if not mail:
                dprint ("Could not read email from: %s" % filepath)
                continue
            
            if not mail.read_headers ():
                dprint ("Could not read email from: %s" % filepath)
                continue
            
            if not mail.message_id:
                dprint ("file '%s' does not contain Message-ID" % filepath)
                continue

            self.emails[mail.message_id] = mail
            mail_count = mail_count + 1
            
        dprint ("%d mails read" % mail_count)
        return True

    def relate (self, first, second):
        if first == second:
            return

        if second in self.conversations:
            self.conversations.remove (second)

        for m in self.conversation[second].emails:
            self.conversation[first].emails.append (m)
            self.conversation[m] = self.conversation[first]
            
    def classify_conversations (self):
        self.conversation = {}
        self.conversations = []

        # initially consider each email to be a differnt converstaion
        for message_id in self.emails.keys ():
            self.conversation[message_id] = Conversation (message_id)
            self.conversations.append (message_id)
            
        # if a this email relates to that email, then this email's 
        # converstaion belongs to that email's converstaion

        thread_topics = {} # to aid 3rd level of precedence
        
        for message_id, mail in self.emails.iteritems ():
            # first relation precedence is 'Reply-To:'
            done = False
            for reply in mail.in_reply_to:
                if self.emails.has_key (reply):
                    self.relate (reply, message_id)
                    done = True
                    break
            if done:
                continue

            # second relation precedence is 'References'
            done = False
            for ref in mail.references:
                if self.emails.has_key (ref):
                    self.relate (ref, message_id)
                    done = True
                    break
            if done:
                continue
            
            # third relation precedence is 'Thread-Topic'
            if mail.thread_topic:
                if mail.thread_topic == mail.subject:
                    if thread_topics.has_key (mail.thread_topic):
                        self.relate (message_id,
                                     thread_topics[mail.thread_topic])
                    thread_topics[mail.thread_topic] = message_id
                else:
                    if thread_topics.has_key (mail.thread_topic):
                        self.relate (thread_topics[mail.thread_topic],
                                     message_id)
                    else:
                        thread_topics[mail.thread_topic] = message_id

        dprint ("Classified into %d conversations" % len (self.conversations))
        return True

    def classify_labels (self):
        self.labels = {}
        return True
    
    def prepare (self):
        if not self.read_headers ():
            dprint ("Could not read headers")
            return False

        if not self.classify_conversations ():
            dprint ("could not classify to conversations")
            return Falsed
#        for cid in self.conversation.keys ():
#            print "--(%s)--" % cid
#            for mid in self.conversation[cid].emails:
 #               print self.emails[mid].subject
        return

def main ():
    mb = Maildir ('/home/avati/Mail/inbox')
    mb.prepare ()
        

main ()
